package com.xd.common.security.config;

import com.xd.common.security.exception.CustomAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;

/**
 * 资源服务器通过 @EnableResourceServer 注解来开启一个 OAuth2AuthenticationProcessingFilter 类型的过滤器
 *
 * @author huboxin
 * @title: AuthResourceConfig
 * @projectName bk
 * @description: 资源服务配置
 * @date 2019/12/3115:18
 */
@Configuration
@EnableResourceServer
//激活方法上的PreAuthorize注解
//@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class XdResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {
    /**
     * 公钥
     */
    private static final String PUBLIC_KEY = "publickey.txt";

    @Resource
    private SecurityAccessDecisionManager securityAccessDecisionManager;

    @Resource
    private AuthenticationEntryPoint authExceptionEntryPoint;

    @Resource
    private CustomAccessDeniedHandler customAccessDeniedHandler;

    @Resource
    private PermitAllUrlProperties permitAllUrl;

    @Resource
    protected RemoteTokenServices remoteTokenServices;

    /**
     * 定义JwtTokenStore，使用jwt令牌
     *
     * @param jwtAccessTokenConverter jwtAccessTokenConverter
     * @return TokenStore
     */
    @Bean
    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }

    /**
     * 定义JJwtAccessTokenConverter，使用jwt令牌
     *
     * @return JwtAccessTokenConverter
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifierKey(getPubKey());
        return converter;
    }


    /**
     *  * 获取非对称加密公钥 Key
     *  * @return 公钥 Key
     *  
     */
    public String getPubKey() {
        ClassPathResource resource = new ClassPathResource(PUBLIC_KEY);
        try {
            InputStreamReader inputStreamReader = new
                    InputStreamReader(resource.getInputStream());
            BufferedReader br = new BufferedReader(inputStreamReader);
            return br.lines()
                    .collect(Collectors.joining("\n"));
        } catch (IOException ioe) {
            return null;
        }
    }


    /**
     * Http安全配置，对每个到达系统的http请求链接进行校验
     *
     * @param http http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry= http.antMatcher("/**")
                .authorizeRequests();

        //禁用csrf ,开启跨域
        http.csrf().disable().cors();

        // 未登录认证异常
        // http.exceptionHandling().authenticationEntryPoint(authExceptionEntryPoint);
        //登录过后访问无权限的接口时自定义响应内容
        //http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler);

        //不创建会话 - 即通过前端传token到后台过滤器中验证是否存在访问权限
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        registry.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
            @Override
            public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                o.setAccessDecisionManager(securityAccessDecisionManager);      // 权限判断
                return o;
            }
        });
        //放开接口
        permitAllUrl.getUrls().forEach(e->registry.antMatchers(e).permitAll());
        // 自动登录 - cookie储存方式
        registry.and().rememberMe();
        // 其余所有请求都需要认证
        registry.anyRequest().authenticated();
        // 防止iframe 造成跨域
        registry.and().headers().frameOptions().disable();
    }


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        //当用户携带token 请求资源服务器的资源时, OAuth2AuthenticationProcessingFilter 拦截token，进行token 和userDetails 过程，把无状态的token 转化成用户信息
        DefaultAccessTokenConverter converter = new DefaultAccessTokenConverter();
        //使用自己的转换器,用来把无状态的token转换为用户信息
        XdUserAuthenticationConverter userAuthenticationConverter
                = new XdUserAuthenticationConverter();
        converter.setUserTokenConverter(userAuthenticationConverter);
        //调用认证服务去check-token,然后转换
        remoteTokenServices.setAccessTokenConverter(converter);
        //配置token 和userDetails 过程,check_token
        resources.tokenServices(remoteTokenServices);
        //登录过后访问无权限的接口时自定义响应内容
        resources.accessDeniedHandler(customAccessDeniedHandler);
        //未登录认证异常
        resources.authenticationEntryPoint(authExceptionEntryPoint);

    }
}
